package com.gitee.huanminabc.jtimerwheel.core;

import com.gitee.huanminabc.jcommon.multithreading.executor.ExecutorUtil;
import com.gitee.huanminabc.jcommon.multithreading.executor.SleepTools;
import com.gitee.huanminabc.jcommon.multithreading.executor.ThreadFactoryUtil;
import com.gitee.huanminabc.jtimerwheel.base.TimerNode;
import com.gitee.huanminabc.jtimerwheel.base.TimerNodeData;
import com.gitee.huanminabc.jtimerwheel.base.TimerResolverCore;
import com.gitee.huanminabc.jtimerwheel.base.TimerWheelLevel;
import com.gitee.huanminabc.jtimerwheel.enums.TimerDynamicStatus;
import com.gitee.huanminabc.jtimerwheel.enums.TimerStatus;
import com.gitee.huanminabc.jtimerwheel.utils.TimerWheelDateEnum;
import com.gitee.huanminabc.jtimerwheel.utils.TimerWheelDateUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @program: java-huanmin-utils
 * @description: 核心
 * @author: huanmin
 * @create: 2025-02-28 18:27
 * <p>
 * 可以利用日志收集然后分析进行任务的统计
 * [TimerWheel:-1] 异常日志<br>
 * [TimerWheel:0] 记录日志<br>
 * [TimerWheel:1] 耗时日志<br>
 * [TimerWheel:2] 经过日志
 **/
@Slf4j
public class TimerWheelCore {
    protected final static TimerWheelLevel wheelMillisecond; //1级时间轮 (毫秒)   1s = 1000ms=1000个槽,按照10毫秒一个槽,那么1s就有100个槽
    protected final static TimerWheelLevel wheelSecond; //2级时间轮 (秒)     1m = 60s=60个槽,按照1秒一个槽,那么1m就有60个槽
    protected final static TimerWheelLevel wheelMinute; //3级时间轮 (分钟)   1h = 60m=60个槽,按照1分钟一个槽,那么1h就有60个槽
    protected final static TimerWheelLevel wheelHour; //4级时间轮 (小时)   1d = 24h=24个槽,按照1小时一个槽,那么1d就有24个槽
    protected final static TimerWheelLevel wheelDay; //5级时间轮 (天)     1天=30个槽,按照1天一个槽,那么1月就有30个槽
    protected final static TimerWheelLevel wheelMonth; //6级时间轮 (月)    1月=12个槽,按照1月一个槽,那么1年就有12个槽
    protected final static TimerWheelLevel wheel_year; //7级时间轮 (年)     年=10000个槽,按照1年一个槽,那么10000年就有10000个槽
    protected final static Map<String, TimerNode> tasks;
    protected final static Lock thread_busyNum; //忙的任务数量线程ID
    protected final static Lock thread_total;  //总的任务数量线程ID

    @Getter
    protected static int busyNum;            // 忙的任务数量
    @Getter
    protected static int total;               // 总的任务数量


    //停止定时器
    protected static boolean runState = true;

    //是否开启过时补偿
    protected static boolean outPayTask = true;

    //是否开始下次任务预测打印
    protected static boolean nextTaskPrint = false;


    protected final static ThreadPoolExecutor executorPool;


    static {
        wheelMillisecond = createTimerWheel(TimerConstant.WHEEL_MILLISECOND_SLOT_NUM, 0);
        wheelSecond = createTimerWheel(TimerConstant.WHEEL_SECOND_SLOT_NUM, 1);
        wheelMinute = createTimerWheel(TimerConstant.WHEEL_MINUTE_SLOT_NUM, 2);
        wheelHour = createTimerWheel(TimerConstant.WHEEL_HOUR_SLOT_NUM, 3);
        wheelDay = createTimerWheel(TimerConstant.WHEEL_DAY_SLOT_NUM, 4);
        wheelMonth = createTimerWheel(TimerConstant.WHEEL_MONTH_SLOT_NUM, 5);
        wheel_year = createYearTimerWheel(TimerConstant.WHEEL_YEAR_SLOT_NUM, 6);
        tasks = new ConcurrentHashMap<>(1024);
        thread_total = new ReentrantLock();
        busyNum = 0;
        total = 0;
        thread_busyNum = new ReentrantLock();
        executorPool = ThreadFactoryUtil.addExecutor("TimerWheelThread", 1024, 1024, Integer.MAX_VALUE);

        ExecutorUtil.newThread(() -> {
            try {
                managerMillisecond();
            } catch (Exception e) {
                log.error("[TimerWheel:-1]异常", e);
            }
        });
        ExecutorUtil.newThread(() -> {
            try {
                managerSecondMinuteHourDayMonthYear();
            } catch (Exception e) {
                log.error("[TimerWheel:-1]异常", e);
            }
        });
        ExecutorUtil.newThread(() -> {
            try {
                saskManager();
            } catch (Exception e) {
                log.error("[TimerWheel:-1]异常", e);
            }
        });

        ExecutorUtil.newThread(() -> {
            try {
                secondTaskExecCheck();
            } catch (Exception e) {
                log.error("[TimerWheel:-1]异常", e);
            }
        });
    }


    private static void saskTransfer(TimerWheelLevel pWheel, TimerWheelLevel nextWheel) throws Exception {
        //判断当前插槽是否有任务,如果有任务那么就下发任务
        //给目标时间轮槽位加锁
        TimerNodeData timerNodeData = pWheel.slotArray.get(pWheel.slotIndex);
        if (!timerNodeData.isEmpty()) {
            timerNodeData.forEachTask((node) -> {
                //将任务下发
                addTimerWheelNode(nextWheel, node);
            });
        }
    }


    //创建时间轮
    private static TimerWheelLevel createTimerWheel(int slotNum, int type) {
        TimerWheelLevel timerWheelLevel = new TimerWheelLevel();
        timerWheelLevel.slotNum = slotNum;
        timerWheelLevel.slotIndex = 0;
        timerWheelLevel.type = type;
        List<TimerNodeData> timerNodeDatas = new ArrayList<>(slotNum);
        int capacity = (type == 0 || type == 1) ? 10000 : 1000;
        for (int i = 0; i < slotNum; i++) {
            timerNodeDatas.add(new TimerNodeData(new ConcurrentHashMap<>(capacity)));
        }
        timerWheelLevel.slotArray = timerNodeDatas;
        return timerWheelLevel;
    }

    private static TimerWheelLevel createYearTimerWheel(int slotNum, int type) {

        Calendar calendar = Calendar.getInstance();
        int saskYear = calendar.get(Calendar.YEAR);
        TimerWheelLevel timerWheelLevel = new TimerWheelLevel();
        timerWheelLevel.slotNum = slotNum;
        timerWheelLevel.slotIndex = saskYear;
        timerWheelLevel.type = type;


        List<TimerNodeData> timerNodeDatas = new ArrayList<>(slotNum);
        //之前的都是空
        for (int i = 0; i < slotNum; i++) {
            timerNodeDatas.add(null);
        }

        //当前年+100
        int endYear = saskYear + 100;
        for (int i = slotNum; i <= endYear; i++) {
            timerNodeDatas.add(new TimerNodeData(new ConcurrentHashMap<>(1024)));
        }
        timerWheelLevel.slotArray = timerNodeDatas;
        return timerWheelLevel;
    }


    private static void threadSaskRun(TimerNode pNode) {

        //执行任务
        try {
            log.info("[TimerWheel:0]任务:{} 开始执行.", pNode.timerId);
            addBusyNum(); //添加忙碌线程数
            pNode.timerDynamic = TimerDynamicStatus.RUN;
            long startTime = System.currentTimeMillis();
            pNode.func.run();
            long endTime = System.currentTimeMillis();
            pNode.timerDynamic = TimerDynamicStatus.AWAIT;
            subBusyNum();//减少忙碌线程数
            log.info("[TimerWheel:1]任务:{} 执行完毕，执行时长: {} 毫秒.", pNode.timerId, (endTime - startTime));
        } catch (Exception e) {
            log.error("[TimerWheel:-1]异常，任务:{} ", pNode.timerId, e);
            //任务执行异常,那么将任务状态设置为停止
            pNode.timerStatus = TimerStatus.STOP;
        }

    }

    private static void addBusyNum() {
        //加锁
        try {
            thread_busyNum.lock();
            busyNum++;
        } finally {
            //解锁
            thread_busyNum.unlock();
        }
    }

    private static void subBusyNum() {
        try {
            //加锁
            thread_busyNum.lock();
            busyNum--;
        } finally {
            //解锁
            thread_busyNum.unlock();
        }
    }

    static void addTotal() {
        //加锁
        Lock threadTotal = thread_total;
        try {
            threadTotal.lock();
            total++;
        } finally {
            //解锁
            threadTotal.unlock();
        }
    }

    private static void subTotal() {
        //加锁
        try {
            thread_total.lock();
            total--;
        } finally {
            //解锁
            thread_total.unlock();
        }
    }


    //根据当前的任务获取下次执行的任务
    private static void nextTimerSask(TimerNodeData pData, TimerNode timerNode) {
        try {
            //获取定时计划执行时间(10位时间戳(秒级))
            long expires = TimerResolverCore.resolver(timerNode.timerResolver);
            timerNode.expires = expires;
            if (expires == 0) {
                timerNode.timerStatus = TimerStatus.DEL;//任务已经执行完毕
                //剔除任务,但是在任务列表中还是存在的,只是时间轮中没有了
                pData.delTask(timerNode.key);
                return;
            }
            timerNodeSaskPrint(timerNode);
            //添加新任务到对应的时间轮里
            addTimerWheel(timerNode);
        } catch (Exception e) {
            log.error("[TimerWheel:-1]异常", e);
        }
    }

    private static void timerNodeSaskPrint(TimerNode timerNode) {
        if (nextTaskPrint) {
            String string = TimerWheelDateUtil.dateTurnString(TimerWheelDateUtil.secondTurnDate(timerNode.expires), TimerWheelDateEnum.DATETIME_PATTERN);
            String format = String.format("[TimerWheel:0]%s任务,预计执行时间: %d(时间戳)---%s(格式化时间)\n", timerNode.timerId, timerNode.expires, string);
            log.info(format);
        }
    }


    //任务添加到时间轮
    static void addTimerWheel(TimerNode timerNode) {
        //获取执行时间的年月日时分秒
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(TimerWheelDateUtil.secondTurnDate(timerNode.expires));
        int saskSecond = calendar.get(Calendar.SECOND);
        int saskMinute = calendar.get(Calendar.MINUTE);
        int saskHour = calendar.get(Calendar.HOUR);
        int saskDay = calendar.get(Calendar.DATE);
        int saskMonth = calendar.get(Calendar.MONTH);
        int saskYear = calendar.get(Calendar.YEAR);
        //获取当前时间的年月日时分秒
        Calendar currentCalendar = Calendar.getInstance();
        int currentSecond = currentCalendar.get(Calendar.SECOND);
        int currentMinute = currentCalendar.get(Calendar.MINUTE);
        int currentHour = currentCalendar.get(Calendar.HOUR);
        int currentDay = currentCalendar.get(Calendar.DATE);
        int currentMonth = currentCalendar.get(Calendar.MONTH);
        int currentYear = currentCalendar.get(Calendar.YEAR);

        //如果任务时间小于当前时间,那么就直接添加到执行的时间轮中
        if (calendar.getTimeInMillis() < currentCalendar.getTimeInMillis()) {
            log.warn("[TimerWheel:2]任务已经到期了,直接添加到执行的时间轮中:{}", timerNode);
            //该执行了,就不继续判断添加了 , 这种一般就属于任务没来得及添加到时间轮中就到期了
            addTimerWheelNode(wheelMillisecond, timerNode);
            return;
        }

        //将任务添加到对应的时间轮中
        // 1.如果任务的年大于等于当前的年
        if (saskYear >= currentYear) {
            // 2.任务的月大于等于当前的月
            if (saskMonth >= currentMonth) {
                // 3.如果任务的日大于等于当前的日
                if (saskDay >= currentDay) {
                    // 4.如果任务的时大于等于当前的时
                    if (saskHour >= currentHour) {
                        // 5.如果任务的分大于等于当前的分
                        if (saskMinute >= currentMinute) {
                            // 6.如果任务的秒大于等于当前的秒
                            if (saskSecond >= currentSecond) {
                                //添加到秒时间轮
                                addTimerWheelNode(wheelSecond, timerNode);
                            } else {
                                //该执行了
                                addTimerWheelNode(wheelMillisecond, timerNode);
                            }
                        } else {
                            //添加到分时间轮
                            addTimerWheelNode(wheelMinute, timerNode);
                        }
                    } else {
                        //添加到时时间轮
                        addTimerWheelNode(wheelHour, timerNode);
                    }
                } else {
                    //添加到天时间轮
                    addTimerWheelNode(wheelHour, timerNode);
                }
            } else {
                //添加到月时间轮
                addTimerWheelNode(wheelMonth, timerNode);
            }
        } else {
            //添加到年时间轮
            addTimerWheelNode(wheel_year, timerNode);
        }
    }


    //创建节点
    protected static TimerNode createTimerNode(String timerResolver, String timer_id, Runnable func) {
        TimerNode timerNode = new TimerNode();
        //解析定时计划
        TimerResolverCore resolver = TimerResolverCore.create_timer_resolver(timerResolver);
        //获取定时计划执行时间(10位时间戳(秒级))
        long expires = TimerResolverCore.resolver(resolver);
        //获取计划类型
        int type = TimerResolverCore.get_plan_type(resolver);
        TimerStatus timer_status = type == 0 ? TimerStatus.STOP : TimerStatus.START;//如果是0那么是停止状态
        timerNode.func = func;
        timerNode.timerResolver = resolver;
        timerNode.expires = expires;
        timerNode.timerId = timer_id;
        timerNode.timerStatus = timer_status;
        timerNode.timerDynamic = TimerDynamicStatus.AWAIT; //默认等待执行
        timerNode.strResolver = timerResolver;//保存原始任务解析字符串
        timerNode.key = UUID.randomUUID().toString();
        timerNodeSaskPrint(timerNode);
        return timerNode;
    }


    //给指定时间轮添加任务
    private static void addTimerWheelNode(TimerWheelLevel wheel, TimerNode timerNode) {
        if (timerNode == null) {
            return;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(TimerWheelDateUtil.secondTurnDate(timerNode.expires));
        //获取时间轮槽索引
        int slotIndex;
        switch (wheel.type) {
            case 0:
                slotIndex = (int) timerNode.expires % TimerConstant.WHEEL_MILLISECOND_SLOT_NUM;
                break;
            case 1:
                slotIndex = calendar.get(Calendar.SECOND);
                break;
            case 2:
                slotIndex = calendar.get(Calendar.MINUTE);
                break;
            case 3:
                slotIndex = calendar.get(Calendar.HOUR);
                break;
            case 4:
                slotIndex = calendar.get(Calendar.DATE);
                break;
            case 5:
                slotIndex = calendar.get(Calendar.MONTH);
                break;
            case 6:
                slotIndex = calendar.get(Calendar.YEAR);
                break;
            default:
                log.error("[TimerWheel:-1]时间轮类型错误:{}", wheel.type);
                return;
        }

        //添加新任务到目标时间轮槽位
        TimerNodeData pData = wheel.slotArray.get(slotIndex);
        pData.addTask(timerNode);
    }


    //任务管理
    private static void saskManager() {
        while (runState) {
            try {
                //遍历所有的任务
                Iterator<Map.Entry<String, TimerNode>> iterator = tasks.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String, TimerNode> timerNodeDataEntry = iterator.next();
                    TimerNode pNode = timerNodeDataEntry.getValue();
                    if (pNode.timerStatus == TimerStatus.NOTARIZE_DEL) {//如果任务状态为删除状态那么就删除任务
                        //删除节点
                        iterator.remove();
                        subTotal();//减少总线任务数
                        System.out.println("=================删除任务:" + pNode.timerId);
                    } else if (pNode.timerStatus == TimerStatus.RESTART) {//重启任务
                        System.out.println("=================" + pNode.timerId + ":,任务重新加载\n");
                        //重新解析定时计划
                        TimerResolverCore timerResolver1 = TimerResolverCore.create_timer_resolver(pNode.strResolver);
                        long expires = TimerResolverCore.resolver(timerResolver1);
                        pNode.timerResolver = timerResolver1;
                        pNode.expires = expires;
                        pNode.timerStatus = TimerStatus.START;//设置任务状态为启动
                        timerNodeSaskPrint(pNode);//打印任务信息
                        //将任务重新加入到对应的时间轮中
                        addTimerWheel(pNode);
                    }
                }
                SleepTools.ms(1000);
            } catch (Exception e) {
                log.error("[TimerWheel:-1]异常", e);
            }
        }
    }

    private static void secondTaskExecCheck() {
        while (runState && outPayTask) {
            long nowTimeSecond = TimerWheelDateUtil.getDateTimeSecond();
            //遍历wheel_second时间轮,判断是否有逾期未执行的
            for (int i = 0; i < 60; i++) {
                TimerNodeData pData = wheelSecond.slotArray.get(i);
                pData.forEachTask((node) -> {
                    if (node.expires < nowTimeSecond && node.timerStatus == TimerStatus.START) {
                        //到期的任务没有执行
                        log.warn("[TimerWheel:2]到期的任务没有执行-进行补偿执行:{}", node);
                        runTaskJob(pData, node, nowTimeSecond);
                    }
                });
            }
            SleepTools.ms(1000); //每1秒检查一次
        }

    }

    private static void managerSecondMinuteHourDayMonthYear() {

        TimerWheelLevel pWheelSecond = wheelSecond;
        TimerWheelLevel pWheelMinute = wheelMinute;
        TimerWheelLevel pWheelHour = wheelHour;
        TimerWheelLevel pWheelDay = wheelDay;
        TimerWheelLevel pWheelMonth = wheelMonth;
        TimerWheelLevel pWheelYear = wheel_year;
        //初始化插槽获取当前时间的秒数
        Calendar calendar = Calendar.getInstance();
        pWheelSecond.slotIndex = calendar.get(Calendar.SECOND);//设置当前插槽的索引
        pWheelMinute.slotIndex = calendar.get(Calendar.MINUTE);//设置当前插槽的索引
        pWheelHour.slotIndex = calendar.get(Calendar.HOUR);//设置当前插槽的索引
        pWheelDay.slotIndex = calendar.get(Calendar.DATE);//设置当前插槽的索引
        pWheelMonth.slotIndex = calendar.get(Calendar.MONTH);//设置当前插槽的索引
        pWheelYear.slotIndex = calendar.get(Calendar.YEAR);//设置当前插槽的索引
        while (runState) {
            try {
                int cuPWheelSecond = pWheelMinute.slotIndex;
                int cuPWheelMinute = pWheelMinute.slotIndex;
                int cuPWheelHour = pWheelHour.slotIndex;
                int cuPWheelDay = pWheelDay.slotIndex;
                int cuPWheelMonth = pWheelMonth.slotIndex;
                int cuPWheelYear = pWheelYear.slotIndex;

                Calendar calendar1 = Calendar.getInstance();
                pWheelSecond.slotIndex = calendar1.get(Calendar.SECOND);
                pWheelMinute.slotIndex = calendar1.get(Calendar.MINUTE);
                pWheelHour.slotIndex = calendar1.get(Calendar.HOUR);
                pWheelDay.slotIndex = calendar1.get(Calendar.DATE);
                pWheelMonth.slotIndex = calendar1.get(Calendar.MONTH);
                pWheelYear.slotIndex = calendar1.get(Calendar.YEAR);


                if (pWheelSecond.slotIndex != cuPWheelSecond) {
                    saskTransfer(pWheelSecond, wheelMillisecond);
                }
                if (pWheelMinute.slotIndex != cuPWheelMinute) {
                    saskTransfer(pWheelMinute, pWheelSecond);
                }
                if (pWheelHour.slotIndex != cuPWheelHour) {
                    saskTransfer(pWheelHour, pWheelMinute);
                }
                if (pWheelDay.slotIndex != cuPWheelDay) {
                    saskTransfer(pWheelDay, pWheelHour);
                }
                if (pWheelMonth.slotIndex != cuPWheelMonth) {
                    saskTransfer(pWheelMonth, pWheelDay);
                }
                if (pWheelYear.slotIndex != cuPWheelYear) {
                    saskTransfer(pWheelYear, pWheelMonth);
                }


                //必须低于500ms避免跳过秒的任务, 导致需要1分钟后才能下发
                SleepTools.ms(400);
            } catch (Exception e) {
                log.error("[TimerWheel:-1]异常", e);
            }


        }
    }

    //定时器管理-(毫秒)(如果插槽里有任务那么就会执行插槽内的所有任务,而任务最多延迟1秒执行)
    private static void managerMillisecond() {

        TimerWheelLevel pWheel = wheelMillisecond;
        //初始化插槽获取当前时间的毫秒数
        pWheel.slotIndex = TimerWheelDateUtil.getDateTimeMillis(new Date()) / 10;//设置当前插槽的索引
        //反复循环
        while (runState) {
            try {
                //执行时间轮的任务
                managerMillisecondSask(pWheel);
                //休眠10毫秒
                SleepTools.ms(10);
                //插槽索引+1
                pWheel.slotIndex = pWheel.slotIndex + 1;
                //如果插槽索引大于插槽数量那么就重置插槽索引
                if (pWheel.slotIndex >= pWheel.slotNum) {
                    pWheel.slotIndex = 0;
                }
            } catch (Exception e) {
                log.error("[TimerWheel:-1]异常", e);
            }
        }
    }

    private static void managerMillisecondSask(TimerWheelLevel wheel) {
        //获取当前插槽
        int slotIndex = wheel.slotIndex;
        //获取当前插槽的节点
        TimerNodeData charKvListData = wheel.slotArray.get(slotIndex);
        if (!charKvListData.isEmpty()) {
            //获取当前时间
            long now = TimerWheelDateUtil.getDateTimeSecond();
            charKvListData.forEachTask((node) -> {
                runTaskJob(charKvListData, node, now);
            });
        }

    }

    private static void runTaskJob(TimerNodeData pData, TimerNode node, long now) {
        //如果当前节点的过期时间小于当前时间,并且状态是可执行那么就执行回调函数
        if (node.expires <= now && node.timerStatus == TimerStatus.START) {
            //重新计算过期时间,并且判断任务是否已经执行完毕,如果任务已经执行完毕,那么就不需要放入时间轮了
            nextTimerSask(pData, node);
            //添加任务到线程池，开始执行本次任务
            executorPool.execute(() -> {
                threadSaskRun(node); //执行回调函数
            });


        }
    }
}
